#!/usr/bin/python
#
#IOApps, IO profiler and IO traces replayer
#
#    Copyright (C) 2010 Jiri Horky <jiri.horky@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#


import sys
from PyQt4 import QtCore
from PyQt4.QtGui import *
from PyQt4.QtCore import * 
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.ticker import FuncFormatter
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from matplotlib.figure import Figure
import numpy as np
import ioapps
import getopt
#import cProfile
from grapher import *

#locale.setlocale(locale.LC_ALL, 'en_US')

    
class MyDelegate(QStyledItemDelegate):
    def __init__(self, parent=None, *args):
        QStyledItemDelegate.__init__(self, parent, *args)

    def paint(self, painter, option, index):
        data = index.data()      
        if data.type() == QVariant.Double:                    
        #if data.type() == QVariant.Date:
            painter.save()            
            if option.state & QStyle.State_Selected:                
                painter.fillRect(option.rect, option.palette.highlight())             
                pen = painter.pen()
                pen.setColor(QApplication.palette().color(QPalette.HighlightedText))
                painter.setPen(pen)
            else:
                painter.fillRect(option.rect, QBrush(index.data(Qt.BackgroundRole)))                                            
            val = data.toPyObject()
            if val > 1:
                formatString = "%0.1f"                
            else:
                formatString = "%0.3f"
                          
            text = locale.format(formatString, val, grouping=False)            
            #text = formatString % val
            painter.drawText(option.rect, Qt.AlignCenter, text)
            painter.restore()
        else:
            QStyledItemDelegate.paint(self, painter, option, index);                
             
class DetailTable(QStandardItemModel):
    """ Table of operations """
    #dataLoaded = QtCore.pyqtSignal()    
    
    def __init__(self, *args):
        QStandardItemModel.__init__(self, *args)
        header = QStringList()
        header.append("Start[s]")        
        header.append("Offset[kiB]")   
        header.append("Size[kiB]")        
        header.append("Dur[ms]")
        self.setHorizontalHeaderLabels(header)    
        self.setSortRole(Qt.DisplayRole)
                
    
    def setData(self, dict):
        timeOpened = dict[0]
        data = dict[1]  
        
            
        #rootItem = self.invisibleRootItem()        
        #self.beginInsertRows(QModelIndex(), 0, len(data)-1)             
        for item in data:
            myList = []
            off = item[0]
            size = item[1]
            start = item[2] - timeOpened
            dur = item[3]
                
            stdItem = QStandardItem()   
            a = QVariant(start)
            stdItem.setData(a, Qt.DisplayRole)            
            myList.append(stdItem)
    
            stdItem = QStandardItem()   
            a = QVariant(off)
            stdItem.setData(a, Qt.DisplayRole)            
            myList.append(stdItem)                    
            
            stdItem = QStandardItem()   
            a = QVariant(size)
            stdItem.setData(a, Qt.DisplayRole)            
            myList.append(stdItem)        
            
            stdItem = QStandardItem()   
            a = QVariant(dur)
            stdItem.setData(a, Qt.DisplayRole)            
            myList.append(stdItem)
                                
            self.appendRow(myList)                                
        #self.dataLoaded.emit()
        self.emit(SIGNAL("dataLoaded"),()) 
                        

class MyTableView(QTableView):
    expandableSection = 0
    
    def __init__(self, parent = None):
        QTableView.__init__(self, parent)
        self.setSortingEnabled(True)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        
    def resizeEvent(self, event):
        self.setUpdatesEnabled(False)
        self.horizontalHeader().setStretchLastSection(False) #To make sure it don't get resized before we compute what we want
        self.horizontalHeader().resizeSections(QHeaderView.ResizeToContents)        
        
        
        headerLen = self.horizontalHeader().length()
        sectionLen = self.horizontalHeader().sectionSize(self.expandableSection)
        viewLen = self.viewport().width()
                    
        if headerLen < viewLen:
            self.horizontalHeader().setStretchLastSection(True)            
        else:
            newLen = sectionLen - (headerLen - viewLen)
            self.horizontalHeader().resizeSection(self.expandableSection, newLen)
            
        self.setUpdatesEnabled(True)
        self.emit(SIGNAL("sizeChanged"),())
        
        
class MyTableWidget(QTableWidget):
    expandableSection = 0
    dataSource = None
    def __init__(self, parent = None):
        QTableWidget.__init__(self, parent)    
        self.setRowCount(1);
        self.setColumnCount(5);
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        
        header = QStringList()
        header.append("#Count")
        header.append("# ops")
        header.append("fileSize[MiB]")        
        header.append("sumSize[MiB]")
        header.append("duration[s]")
        self.setHorizontalHeaderLabels(header)
        self.setMaximumHeight(60)                    
        self.setSortingEnabled(False)
        self.setEditTriggers(QAbstractItemView.NoEditTriggers)        
            
    def resizeEvent(self, event):
        self.setUpdatesEnabled(False)
        self.horizontalHeader().setStretchLastSection(False) #To make sure it don't get resized before we compute what we want
        self.horizontalHeader().resizeSections(QHeaderView.ResizeToContents)        
        
        
        headerLen = self.horizontalHeader().length()
        sectionLen = self.horizontalHeader().sectionSize(self.expandableSection)
        viewLen = self.viewport().width()
                    
        if headerLen < viewLen:
            self.horizontalHeader().setStretchLastSection(True)            
        else:
            newLen = sectionLen - (headerLen - viewLen)
            self.horizontalHeader().resizeSection(self.expandableSection, newLen)
            
        self.setUpdatesEnabled(True)
    def setDataSource(self, dataSource):
        self.dataSource = dataSource
    
    def recomputeData(self):
        ops = 0
        fileSize = 0
        sumSize = 0        
        dur = 0
        count = self.dataSource.rowCount()
        for i in range(0, count):            
            ops += int(self.dataSource.index(i, 1).data().toPyObject())
            fileSize += float(self.dataSource.index(i, 2).data().toPyObject() / 1024)
            sumSize += float(self.dataSource.index(i, 3).data().toPyObject() / 1024)
            dur += float(self.dataSource.index(i, 4).data().toPyObject() / 1000)
        self.setItem(0,0, QTableWidgetItem(QString.number(count)));
        self.setItem(0,1, QTableWidgetItem(QString.number(ops)))
        self.setItem(0,2, QTableWidgetItem(QString.number(fileSize)))
        self.setItem(0,3, QTableWidgetItem(QString.number(sumSize)))
        self.setItem(0,4, QTableWidgetItem(QString.number(dur)))
                    
        
        
        
#class NameFilterProxyModel(QSortFilterProxyModel):
#    def __init(self, column = 0, parent = None):
#        QSortFilterProxyModel.__init__(self,parent)         
# int source_row, const QModelIndex & source_parent         
#    def filterAcceptsRow(self, row, parent):
        

class DetailDialog(QDialog):
    reads = "reads";
    writes = "writes";
    def __init__(self, fileName, reads, dataHolder, parent = None):
        QDialog.__init__(self, parent);                   
        if reads:
            self.mode = self.reads;
        else:
            self.mode = self.writes;
             
        self.fileName = fileName
        self.dataHolder = dataHolder
        self.setWindowTitle(fileName + " - " + self.mode)
        
        self.grapher = SubGrapher()
        if reads:
            self.grapher.setType(self.grapher.READS)
        else:
            self.grapher.setType(self.grapher.WRITES)
        self.grapher.setName(self.fileName)
        self.grapher.setTimeMode('relfile')
        self.grapher.setDataHolder(dataHolder)
        
        self.highlightedItems = []
        self.standardBrush = None        
                                  
        # Create the mpl Figure and FigCanvas objects. 
        # 5x4 inches, 72 dots-per-inch
        #
        self.dpi = 72
        self.fig = Figure((6.0, 5.0), dpi=self.dpi)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setParent(self)
        self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        
        #our table:
        self.model = None
        self.view = QTableView()                        
        self.view.setSortingEnabled(True)
        self.view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.view.horizontalHeader()
        self.view.setSelectionBehavior(QAbstractItemView.SelectRows)
        delegate = MyDelegate()
        self.view.setItemDelegate(delegate)        
        self.view.setMinimumWidth(415)
        self.view.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        
    
        # Since we have only one plot, we can use add_axes 
        # instead of add_subplot, but then the subplot
        # configuration tool in the navigation toolbar wouldn't
        # work.
        #
        self.axes = self.fig.add_subplot(111)
        self.grapher.setAxes(self.axes)
        self.grapher.setFig(self.fig)
                
        self.canvas.mpl_connect('pick_event', self.on_pick)            
        self.mpl_toolbar = NavigationToolbar(self.canvas, self)
        
        # Other GUI controls
        #                         
        self.draw_button = QPushButton("&Highlight selected")        
        QObject.connect(self.draw_button, SIGNAL("clicked()"), self.__highlightSelected)
        QObject.connect(self.draw_button, SIGNAL("clicked()"), self.__highlightSelected)
        self.displayStatistics = QCheckBox("Display statistics")
        QObject.connect(self.displayStatistics, SIGNAL("stateChanged(int)"), self.__showPlot)                               
        
        plot_label = QLabel('Type of plot:')        
        
        self.patternButton = QRadioButton("Pattern diagram")
        self.histSizeButton = QRadioButton("Size histogram")
        self.histTimeButton = QRadioButton("Time histogram")
        self.histSpeedButton = QRadioButton("Speed histogram")        
        
    
        self.patternButton.setChecked(True)
        QObject.connect(self.patternButton, SIGNAL("toggled(bool)"), self.__showPlot)
        QObject.connect(self.histSizeButton, SIGNAL("toggled(bool)"), self.__showPlot)
        QObject.connect(self.histTimeButton, SIGNAL("toggled(bool)"), self.__showPlot)
        QObject.connect(self.histSpeedButton, SIGNAL("toggled(bool)"), self.__showPlot)         
                    
        
        #
        # Layout with box sizers
        # 
        hbox = QHBoxLayout()
        
        hbox.addWidget(self.draw_button)
        hbox.addWidget(self.displayStatistics);
        hbox.addStretch()
        for w in [plot_label, self.patternButton, self.histSizeButton, self.histTimeButton, self.histSpeedButton]:
            hbox.addWidget(w)
            hbox.setAlignment(w, Qt.AlignVCenter)
        
        vvbox = QVBoxLayout()
        vvbox.addWidget(self.canvas)
        vvbox.addWidget(self.mpl_toolbar)
        
        hhbox = QHBoxLayout()
        hhbox.addWidget(self.view)        
        hhbox.addLayout(vvbox)
        
        vbox = QVBoxLayout()
                        
        vbox.addLayout(hhbox)                
        vbox.addLayout(hbox)        
        self.setLayout(vbox)
                
        self.__showPlot()
        
    def __clearHighlighted(self):
        if self.standardBrush:
            for item in self.highlightedItems:                               
                item.setBackground(self.standardBrush)
    
    def __highlightRows(self, rows):
        if self.standardBrush == None: #this is the first time we are called
            self.standardBrush = self.view.model().item(0,0).background()
        l = []
        brush = QBrush(Qt.red)
        for row in rows:
            for i in xrange(self.view.model().columnCount()):
                item = self.view.model().item(row, i)    
                item.setBackground(brush)
                l.append(item);
        return l
                
    def __highlightSelected(self):
        self.__clearHighlighted();
        
        indexes = self.view.selectedIndexes()
        rows = []     
        for index in indexes:
            rows.append(index.row())
        rowset = set(rows);
        
        self.highlightedItems = self.__highlightRows(rowset)
        self.__highlightLines(rowset, self.grapher.lineCollection())
        self.__highlightRows(rowset)
             
        self.canvas.draw()
        
    def __highlightLines(self, lines, artist):        
        cmap = []
        for i in xrange(len(artist.get_paths())):
            if i in lines:
                cmap.append('r')
            else:
                cmap.append('b')            
        artist.set_color(cmap)
                
    def setModel(self, model):
        self.model = model
        self.connect(self.model, SIGNAL("dataLoaded()"), self.view.resizeColumnsToContents)
        self.connect(self.model, SIGNAL("dataLoaded()"), self.view.resizeRowsToContents)        
        #model.dataLoaded.connect(self.view.resizeColumnsToContents)
        #model.dataLoaded.connect(self.view.resizeRowsToContents)        
        self.view.setModel(self.model)     
        self.view.resizeColumnsToContents()
        self.view.resizeRowsToContents()        
    
    def on_pick(self, event):
        # The event received here is of the type
        # matplotlib.backend_bases.PickEvent
        #
        # It carries lots of information, of which we're using
        # only a small amount here.
        #         
        if not self.pickEnabled:
            return;
        
        indexes = []
        print "event ind is", event.ind        
        for i in event.ind:
            path = event.artist.get_paths()[i]            
            for t in path.iter_segments():
                startTime = t[0][1] / 1000         
                modelIndex = self.view.model().index(0,0)
                startTime = str(startTime)[:7]        
                inds = self.view.model().match(modelIndex, Qt.DisplayRole, startTime)
                indexes += inds                                
                break; #we know that the Path consists by one line, so this is sufficient
            
        #highlight it 
        self.__highlightLines(event.ind, event.artist)        
            
        self.view.scrollTo(indexes[0], QAbstractItemView.PositionAtCenter)                 
        
            
        self.__clearHighlighted();        
        
        rows = []          
        for index in indexes:
            row = index.row()
            rows.append(row)
            
        self.highlightedItems = self.__highlightRows(set(rows))                    
        self.canvas.draw()        
        
    def plotData(self):            
        self.grapher.setNames(self.fileName)
        if self.mode == self.reads:
            self.grapher.plotReads()
        else: 
            self.grapher.plotWrites()      
          
               
    def __showPlot(self):
        """ Redraws the figure
        """              
        if self.patternButton.isChecked():
            self.pickEnabled = True
            if self.mode == self.reads:
                self.grapher.plotReads(self.displayStatistics.isChecked())
            else: 
                self.grapher.plotWrites(self.displayStatistics.isChecked())            
        elif self.histSizeButton.isChecked():            
            self.grapher.plotHistSize()
            self.pickEnabled = False
            self.__clearHighlighted()
        elif self.histTimeButton.isChecked():
            self.grapher.plotHistTime()
            self.pickEnabled=  False
            self.__clearHighlighted()
        elif self.histSpeedButton.isChecked():
            self.grapher.plotHistSpeed()
            self.pickEnabled=  False
            self.__clearHighlighted()
        self.canvas.draw()   
    
        
class CentralWidget(QWidget):    
    READS = "reads"
    WRITES = "writes"
    
    def printing(self, a, b):
        print "changed"    
    
    def __init__(self, parent = None):
        QWidget.__init__(self, parent)
    
        self.connect(QCoreApplication.instance(), SIGNAL("aboutToQuit()"), self.__clean)
        #QCoreApplication.instance().aboutToQuit.connect(self.__clean)
    
        self.showing = None
        self.fileName = None
        self.dialogs = []                                                        
                
        self.dataHolder = DataHolder()
        
        self.model = MyTable(0,0, self)
        self.filter = QSortFilterProxyModel(self)
        self.filter.setSourceModel(self.model)
        
        self.tableW = MyTableWidget(self)        
        self.tableW.setDataSource(self.filter)            
                                
        #self.setSelectionBehavior(QAbstractItemView.SelectRows)   
        
        lineEdit = QLineEdit('Name regexp filter', self)
        self.connect(lineEdit, SIGNAL("textChanged(QString)"), self.filter.setFilterRegExp)   
        #lineEdit.textChanged.connect(self.filter.setFilterRegExp)
                    
        self.view = MyTableView()
        self.view.setModel(self.filter)        
        #self.view.horizontalHeader().setStretchLastSection(True)
                
        
            
        delegate = MyDelegate()
        self.view.setItemDelegate(delegate)
                        
        self.buttonD = QPushButton("Details", self)
        
        layout = QHBoxLayout()
        self.readRadioButton = QRadioButton("Reads")
        self.writeRadioButton = QRadioButton("Writes")
        self.connect(self.readRadioButton, SIGNAL("toggled(bool)"), self.__showTable)
        self.connect(self.writeRadioButton, SIGNAL("toggled(bool)"), self.__showTable)
        #self.readRadioButton.toggled.connect(self.__showTable)
        #self.writeRadioButton.toggled.connect(self.__showTable)
        layout.addWidget(self.readRadioButton)
        layout.addWidget(self.writeRadioButton)
        self.readRadioButton.setChecked(True)
        layout.addWidget(self.buttonD)
                        
        self.connect(self.buttonD, SIGNAL("clicked()"), self.showDetails)                        
        #buttonD.clicked.connect(self.showDetails)                            
                
        vlayoutMain = QVBoxLayout(self)
        vlayoutMain.addWidget(self.view)
        vlayoutMain.addWidget(self.tableW)       
        vlayoutMain.addWidget(lineEdit)        
        vlayoutMain.addLayout(layout)

    def __clean(self):
        for diag in self.dialogs:
            diag.close();

    def showDetails(self):        
        indexes = self.view.selectedIndexes()
        rows = []     
        for index in indexes:
            rows.append(self.filter.mapToSource(index).row())
        rowset = set(rows)

        if not len(rowset) == 1:
            QMessageBox.warning(self, "Warning", "Exactly one file has to be specified.", );
            return
        
        fileName = self.model.getNames(rowset)[0]
        if self.readRadioButton.isChecked():            
            data = self.dataHolder.data()[self.READS][fileName]            
            reads = True            
        else:
            data = self.dataHolder.data()[self.WRITES][fileName]
            reads = False
                                
        dialog = DetailDialog(fileName, reads, self.dataHolder, self)
        model = DetailTable(0,0, self)
        model.setData(data)
        dialog.setModel(model)
        
        dialog.show()
        self.dialogs.append(dialog)
        self.connect(dialog, SIGNAL("finished(int)"), self.__dialogClosed)
        #dialog.finished.connect(self.__dialogClosed)
    
    def __dialogClosed(self, retVal):        
        dial = self.sender()
        self.dialogs.remove(dial)                
        
    def newFileOpened(self, fileName):
        if not self.fileName == None:
            model = self.filter.sourceModel(); # detach for performance reasons
            modelView = self.view.model();        
            self.filter.setSourceModel(None)
            self.view.setModel(None)
            self.model.clear()  #clear
            self.filter.setSourceModel(model)
            self.view.setModel(modelView) # attach again
            ioapps.finish()
                        
             
        self.fileName = fileName
        if fileName.endsWith(".bin"):
            ioapps.init_items_bin(fileName.toLocal8Bit().data());
        else:
            ioapps.init_items_strace(fileName.toLocal8Bit().data());
        
        ioapps.simulate();        
        ioapps.free_items();        
        
        reads = ioapps.get_reads()
        writes = ioapps.get_writes()        
        self.dict = {}
        self.dict[self.READS] = reads;
        self.dict[self.WRITES] = writes;
        self.dataHolder.setData(self.dict)                                          
        self.__showTable()        
        self.tableW.recomputeData()
        self.filter.rowsRemoved.connect(self.tableW.recomputeData)        
        self.filter.rowsInserted.connect(self.tableW.recomputeData)                    
        
    def __showTable(self):        
        if not self.fileName:
            return            
            
        if self.readRadioButton.isChecked():
            if self.showing == self.READS: #already showing reads
                return 
            sumDict = self.dataHolder.getSummary(self.READS)
            self.showing = self.READS
        else:
            if self.showing == self.WRITES:
                return
            self.showing = self.WRITES            
            sumDict = self.dataHolder.getSummary(self.WRITES)
        
        model = None
            
        if not self.showing == None:
            model = self.filter.sourceModel()
            self.filter.setSourceModel(None) #for performance reasons
            self.view.setModel(None) #detach model completely for performance reasons?                      
            self.model.clear()
            self.showing = None              
                      
        for k,v in sumDict.iteritems():
            list = [k] + v
            self.model.addRow(list)
                
        if self.filter.sourceModel() == None:
            self.filter.setSourceModel(model)
        
        if self.view.model() == None:
            self.view.setModel(self.filter)            
        
        self.tableW.recomputeData()
        

class MainWindow(QMainWindow):
    appName = "RepIO GUI"
    
    def __init__(self, parent = None):
        QMainWindow.__init__(self, parent)

        self.fileName = None        
        
        self.resize(800, 600)        
        self.setWindowTitle(self.appName)
        
        open = QAction(QIcon('icons/open.png'), '&Open', self)
        open.setShortcut('Ctrl+O')
        open.setStatusTip('Load file containing strace records')
        self.connect(open, SIGNAL('triggered()'), self.slotFile)
        
        exit = QAction(QIcon('icons/exit.png'), '&Exit', self)
        exit.setShortcut('Ctrl+W')
        exit.setStatusTip('Exit application')
        self.connect(exit, SIGNAL('triggered()'), self.close)
            
        self.setWindowIcon(QIcon('/usr/kde/3.5/share/icons/default.kde/64x64/devices/hdd_unmount.png'))
             
        self.statusBar()
        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(open)
        file.addAction(exit)
        
        aboutQt = QAction(QIcon('icons/about.png'), '&About Qt', self)
        aboutQt.setStatusTip('Show about Qt dialog')
        self.connect(aboutQt, SIGNAL('triggered()'), self.slotAboutQt)
        
        self.centralW = CentralWidget()
        self.setCentralWidget(self.centralW)
        
        help = menubar.addMenu('&About')
        help.addAction(aboutQt)
                        
        
        
    def slotFile(self):
        fileName = QFileDialog.getOpenFileName(self, "", "~/school/diplomka/replicator/", "Repio binary files (*.bin);;Pure strace output (*.*)")        
        if fileName:
            self.fileName = fileName            
            self.updateWindowTitle()
            self.centralW.newFileOpened(self.fileName)
        
    def slotAboutQt(self):
        QMessageBox.aboutQt(self)
    
    def updateWindowTitle(self):
        if self.fileName:
            self.setWindowTitle(self.appName + " - " + self.fileName)
        
            
class MyTable(QStandardItemModel):
    """ Table of operations """    
    
    def __init__(self, *args):
        QStandardItemModel.__init__(self, *args)    
        self.setSortRole(Qt.DisplayRole)    
        self.__setHeader()
    
    def __setHeader(self):
        header = QStringList()
        header.append("Name")
        header.append("# ops")
        header.append("fileSize[kiB]")        
        header.append("sumSize[kiB]")
        header.append("Dur[ms]")
        self.setHorizontalHeaderLabels(header)
            
    def clear(self):
        QStandardItemModel.clear(self)
        self.__setHeader()        
    
    def addRow(self, list):        
        myList = []
        for item in list: 
            #print "item", i, item        
            stdItem = QStandardItem()   
            if type(item) in [np.float64, np.float64]: # as of PyQt 4.7, we have specifically convert numpy.float* numbers to python's float, to make sure QVariant understands it
                a = QVariant(float(item))
            else:
                a = QVariant(item)
            stdItem.setData(a, Qt.DisplayRole)            
            myList.append(stdItem)               
        self.appendRow(myList)        
        
    def getNames(self, rows):
        names = []
        for row in rows:
            names.append(self.item(row).data(Qt.DisplayRole).toPyObject().toLocal8Bit().data())
            
        return names

if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    app.exec_()
    #cProfile.run('app.exec_()', '/tmp/profile')
    sys.exit(0)
    sys.exit(app.exec_())
    
